package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"text/template"

	"github.com/pkg/errors"
)

func makeDefine() (err error) {
	f, err := os.Open(path.Join(srcPath, "ThostFtdcUserApiDataType.h"))
	if err != nil {
		return errors.Wrap(err, "open file datatype.h")
	}
	// 先手动转成 utf8 再用,否则后面 go中C引用会有问题
	bs, _ := ioutil.ReadAll(f)

	// TODO 类型定义
	type datatype struct {
		Remark, Name, Type string
		Len                int
	}
	types := make([]*datatype, 0)
	/*/////////////////////////////////////////////////////////////////////////
	///TFtdcContentType是一个消息正文类型
	/////////////////////////////////////////////////////////////////////////
	typedef char TThostFtdcContentType[501];*/
	re, err := regexp.Compile(`///.+是一个(.*)\r\n/*\r\ntypedef\s+(\w+)\s+(\w+)\[(\d+)\];`)
	if err != nil {
		fmt.Print(err)
		return err
	}
	defs := re.FindAllStringSubmatch(string(bs), -1)
	/*/////////////////////////////////////////////////////////////////////////
	///TFtdcOrderActionRefType是一个报单操作引用类型
	/////////////////////////////////////////////////////////////////////////
	typedef int TThostFtdcOrderActionRefType;*/
	re, _ = regexp.Compile(`///.+是一个(.*)\r\n/*\r\ntypedef\s+(\w+)\s+(\w+);`) // 除字串外的类型
	defs = append(defs, re.FindAllStringSubmatch(string(bs), -1)...)         // 合并

	for _, def := range defs {
		var goType string
		var byteLen = 0
		switch def[2] {
		case "char":
			goType = "byte"
			if len(def) > 4 {
				byteLen, err = strconv.Atoi(def[4])
				if err != nil {
					return errors.Wrap(err, "Atoi")
				}
			}
		case "int":
			goType = "int32"
		case "double":
			goType = "float64"
		case "short":
			goType = "int16"
		default:
			return errors.New("未知类型")
		}
		types = append(types, &datatype{
			Remark: def[1],
			Type:   goType,
			Name:   def[3],
			Len:    byteLen,
		})
	}
	//  $1\ntype $2 [$3]byte
	tmpl, err := template.New("type").Parse(`// {{.Remark}}
type {{.Name}} {{if gt .Len 0}}[{{.Len}}]{{end}}{{.Type}}`)
	if err != nil {
		return errors.Wrap(err, "new template")
	}
	var defines string
	mv := make(map[string]struct{}) // 用于去重
	for _, v := range types {
		if _, ok := mv[v.Name]; ok {
			continue
		}
		mv[v.Name] = struct{}{} // 用于去重
		buf := bytes.Buffer{}
		tmpl.Execute(&buf, *v)
		defines += buf.String() + "\n"
		// fmt.Println(buf.String())
	}
	// TODO 枚举
	/*/////////////////////////////////////////////////////////////////////////
	///TFtdcTraderConnectStatusType是一个交易所交易员连接状态类型
	/////////////////////////////////////////////////////////////////////////
	///没有任何连接
	#define THOST_FTDC_TCS_NotConnected '1'
	///已经连接
	#define THOST_FTDC_TCS_Connected '2'
	///已经发出合约查询请求
	#define THOST_FTDC_TCS_QryInstrumentSent '3'
	///订阅私有流
	#define THOST_FTDC_TCS_SubPrivateFlow '4'

	typedef char TThostFtdcTraderConnectStatusType;*/
	re, _ = regexp.Compile(`///.+是一个(.*)\r\n/*\r\n([^;]*)\r\n\r\ntypedef\s+char\s+(\w+);`) // 注意 [^;]与 [nn]char类型区分
	for _, defs := range re.FindAllStringSubmatch(string(bs), -1) {
		defines += fmt.Sprintf("//%s\ntype %s byte\n", defs[1], defs[3])
		enums := defs[2]
		re = regexp.MustCompile(`///(.*)\r\n#define\s+(\w+)\s+'(.+)'`) // 注释 名称 值 \w改为.因为有'#'的情况
		// line := re.ReplaceAllString(string(bs), "// $1\nconst $2 = '$3'\n")
		for _, v := range re.FindAllStringSubmatch(enums, -1) {
			var line string
			if len(v[3]) == 1 {
				line = fmt.Sprintf("// %s\nconst %s = '%s'", v[1], v[2], v[3])
			} else { // 银转类型为6位数字
				line = fmt.Sprintf("// %s\nconst %s = \"%s\"", v[1], v[2], v[3])
			}
			defines += line + "\n"
			// fmt.Println(line)
		}
	}
	content := fmt.Sprintf("package define\n%s", defines)
	err = os.WriteFile("../define/datatype.go", []byte(content), os.ModePerm)
	if err != nil {
		return errors.Wrap(err, "write define")
	}
	return nil
}

func makeStruct() (err error) {
	f, err := os.Open(path.Join(srcPath, "ThostFtdcUserApiStruct.h"))
	if err != nil {
		return errors.Wrap(err, "read struct")
	}

	// 汉字处理
	bs, _ := ioutil.ReadAll(f) // 汉字处理
	/*
		///信息分发
		struct CThostFtdcDisseminationField
		{
			///序列系列号
			TThostFtdcSequenceSeriesType	SequenceSeries;
			///序列号
			TThostFtdcSequenceNoType	SequenceNo;
		};
	*/
	var strStruc string
	re := regexp.MustCompile(`///(\S*)\s*struct\s*(\w*)\s*{([^}]*)}`) // 分成struct的注释,名称,字段两部分
	for _, strc := range re.FindAllStringSubmatch(string(bs), -1) {
		remark := strc[1]
		structName := strc[2]
		fields := strc[3]
		strStruc += fmt.Sprintf("// %s\ntype %s struct{\n", remark, structName)
		re = regexp.MustCompile(`///([^\r\n]*)\s*(\w+)\s+([^;]+);`) // 所有字段再分解成各个单独字段: 注释(可能含空格),类型,名称
		strStruc += re.ReplaceAllString(fields, "// $1\n\t$3 $2\n")
		strStruc += "}\n\n"
	}
	content := fmt.Sprintf("package define\n%s", strStruc)
	err = os.WriteFile("../define/structs.go", []byte(content), os.ModePerm)
	if err != nil {
		return errors.Wrap(err, "write structs")
	}
	return nil
}

func makeCallBackType(apiFolder string, quote bool) (err error) {
	// TODO Reg 注册函数-C
	var cStr string = `/*
#cgo CPPFLAGS: -fPIC -I../` + apiFolder

	if quote {
		cStr += `
#cgo linux LDFLAGS: -fPIC -L${SRCDIR} -Wl,-rpath ${SRCDIR} -lquote -lstdc++

#include "ThostFtdcUserApiDataType.h"
#include "ThostFtdcUserApiStruct.h"
void* qCreateApi();
void* qCreateSpi();
void* qRegisterSpi(void*, void*);
`
	} else {
		cStr += `
#cgo linux LDFLAGS: -fPIC -L${SRCDIR} -Wl,-rpath ${SRCDIR} -ltrade -lstdc++

#include "ThostFtdcUserApiDataType.h"
#include "ThostFtdcUserApiStruct.h"
void* tCreateApi();
void* tCreateSpi();
void* tRegisterSpi(void*, void*);`
	}

	var tepl *template.Template
	for _, fd := range GOLOBAL_FunDefs {
		if strings.HasPrefix(fd.FuncName, "On") { // INFO On 函数
			//void qSetOnRspUserLogin(void*, void*);
			//int qRspUserLogin(struct CThostFtdcRspUserLoginField *pRspUserLogin, struct CThostFtdcRspInfoField *pRspInfo, int nRequestID, _Bool bIsLast);
			if quote {
				tepl, _ = template.New("on_c").Parse(`void qSet{{ .FuncName }}(void*, void*);
int q{{ .FuncName }}({{ if gt (len .FuncParams) 0 }}{{ .FuncParams }}{{ end }});`)
			} else { // TRADE
				tepl, _ = template.New("on_c").Parse(`void tSet{{ .FuncName }}(void*, void*);
int t{{ .FuncName }}({{ if gt (len .FuncParams) 0 }}{{ .FuncParams }}{{ end }});`)
			}
			buf := bytes.Buffer{}
			tepl.Execute(&buf, *fd)
			// struct 前加 'struct'
			cStr += buf.String()
			cStr += "\n"
		} else { // INFO 主调函数
			if fd.FuncName == "RegisterSpi" { // 特别处理
				continue
			}
			//void qReqUserLogin(void*, struct CThostFtdcReqUserLoginField*, int);
			if quote {
				tepl, _ = template.New("func_c").Parse(`void q{{.FuncName}}(void*{{ if gt (len .FuncParams) 0 }}, {{.FuncParams}}{{end}});`)
			} else { // TRADE
				tepl, _ = template.New("func_c").Parse(`void t{{.FuncName}}(void*{{ if gt (len .FuncParams) 0 }}, {{.FuncParams}}{{end}});`)
			}
			buf := bytes.Buffer{}
			tepl.Execute(&buf, *fd)
			cStr += buf.String()
			cStr += "\n"
		}
	}
	cStr = strings.ReplaceAll(cStr, "CThostFtdc", "struct CThostFtdc")
	cStr = strings.ReplaceAll(cStr, " bool ", " _Bool ") // bool =>  _Bool
	// fmt.Println(cStr)

	/*//export qRspUserLogin
	func qRspUserLogin(field *C.struct_CThostFtdcRspUserLoginField, info *C.struct_CThostFtdcRspInfoField, i C.int, b C._Bool)  {
		loginField := (*define.CThostFtdcRspUserLoginField)(unsafe.Pointer(field))
		infoField := (*define.CThostFtdcRspInfoField)(unsafe.Pointer(info))
		q.HFQuote.RspUserLogin(loginField, infoField)
	}*/
	// TODO 响应函数定义
	var cbFuns string
	for _, fd := range GOLOBAL_FunDefs {
		if !strings.HasPrefix(fd.FuncName, "On") {
			continue
		}
		if quote {
			tepl, _ = template.New("cb").Parse(`//export q{{.FuncName}}
func q{{.FuncName}}(%s) C.int  {
	%s
	fmt.Print(%s)
	return 0
}
`)
		} else { // TRADE
			tepl, _ = template.New("cb").Parse(`//export t{{.FuncName}}
func t{{.FuncName}}(%s) C.int  {
	%s
	fmt.Print(%s)
	return 0
}
`)
		}
		buf := bytes.Buffer{}
		tepl.Execute(&buf, *fd)
		var params string        // 参数 ()中的部分
		var structCase string    // 转换结构体 {}之间的部分
		var printParams string   // 打印的变量名
		if fd.FuncParams != "" { // 参数可能为空
			re := regexp.MustCompile(`(\w+)\s+[*]?(\w+)[,]?\s*`) //参数分解:类型,名称
			ps := re.FindAllStringSubmatch(fd.FuncParams, -1)
			for _, p := range ps {
				paramType := p[1]
				paramName := p[2]
				if strings.HasPrefix(paramType, "CThost") { // struct
					params += fmt.Sprintf("%s *C.struct_%s,", paramName, paramType)
					structCase += fmt.Sprintf("%sField := (*define.%s)(unsafe.Pointer(%s))\n", paramName, paramType, paramName)
					printParams += paramName + "Field,"
				} else {
					switch paramType {
					case "int":
						params += "i C.int,"
					case "bool":
						params += "b C._Bool,"
					}
				}
			}
			params = strings.TrimSuffix(params, ",")
			structCase = strings.TrimSuffix(structCase, "\n")
			printParams = strings.TrimSuffix(printParams, ",")
		}
		cbFuns += fmt.Sprintf(buf.String(), params, structCase, printParams)
	}
	// TODO 函数封装
	var reqFuncs string
	if quote {
		reqFuncs = `q.HFQuote.ReqConnect = func(addr string) {
		front := C.CString(addr)
		C.qRegisterFront(q.api, front)
		defer C.free(unsafe.Pointer(front))
		C.qInit(q.api)
		// C.qJoin(q.api)
	}
	q.HFQuote.Release = func() {
		C.qRelease(q.api)
	}
	q.HFQuote.ReqUserLogin = func(f *define.CThostFtdcReqUserLoginField) {
		C.qReqUserLogin(q.api, (*C.struct_CThostFtdcReqUserLoginField)(unsafe.Pointer(f)), C.int(1))
	}
	q.HFQuote.ReqSubscript = func(instrument string) {
		ppInstrumentID := make([]*C.char, 1)
		ppInstrumentID[0] = (*C.char)(unsafe.Pointer(C.CString(instrument)))
		C.qSubscribeMarketData(q.api, (**C.char)(unsafe.Pointer(&ppInstrumentID[0])), C.int(1))
	}
	q.HFQuote.Init() // 初始化

	C.qSetOnFrontConnected(spi, C.qOnFrontConnected)
	C.qSetOnFrontDisconnected(spi, C.qOnFrontDisconnected)
	C.qSetOnRspUserLogin(spi, C.qOnRspUserLogin)
	C.qSetOnRtnDepthMarketData(spi, C.qOnRtnDepthMarketData)`
	} else {
		reqFuncs = `t.HFTrade.ReqConnect = func(addr string) {
	front := C.CString(addr)
	C.tRegisterFront(t.api, front)
	defer C.free(unsafe.Pointer(front))
	C.tInit(t.api)
	// C.qJoin(t.api)
	}
	t.HFTrade.Release = func() {
		C.tRelease(t.api)
	}
	t.HFTrade.ReqUserLogin = func(f *define.CThostFtdcReqUserLoginField) {
		C.tReqUserLogin(t.api, (*C.struct_CThostFtdcReqUserLoginField)(unsafe.Pointer(f)), C.int(1))
	}
	t.HFTrade.Init() // 初始化

	C.tSetOnFrontConnected(spi, C.tOnFrontConnected)
	C.tSetOnFrontDisconnected(spi, C.tOnFrontDisconnected)
	C.tSetOnRspUserLogin(spi, C.tOnRspUserLogin)`
	}

	// TODO 合成
	var msg string
	if quote {
		msg = fmt.Sprintf(`package lnx
%s
#include <stdlib.h>
#include <stdint.h>
*/
import "C"

// Quote 行情接口
type Quote struct {
	gosopt.HFQuote // 组合

	api unsafe.Pointer
}

var q *Quote

// NewQuote new md api instanse
func NewQuote() *Quote {
	q = new(Quote)

	q.api = C.qCreateApi()
	spi := C.qCreateSpi()
	C.qRegisterSpi(q.api, spi)

	// req
	%s
	return q
}
// reg
%s
`, cStr, reqFuncs, cbFuns)
	} else { // Trade
		msg = fmt.Sprintf(`package lnx
%s
#include <stdlib.h>
#include <stdint.h>
*/
import "C"

// Trade 交易接口
type Trade struct {
	gosopt.HFTrade // 组合

	api unsafe.Pointer
}

var t *Trade

// NewTrade new trade api instanse
func NewTrade() *Trade {
	t = new(Trade)

	t.api = C.tCreateApi()
	spi := C.tCreateSpi()
	C.tRegisterSpi(t.api, spi)

	// req
	%s
	return t
}
// reg
%s
`, cStr, reqFuncs, cbFuns)
	}
	if quote {
		err = os.WriteFile("../lnx/quote.go", []byte(msg), os.ModePerm)
	} else {
		msg = strings.ReplaceAll(msg, "THOST_TE_RESUME_TYPE", "int") // THOST_TE_RESUME_TYPE 类型无法识别
		err = os.WriteFile("../lnx/trade.go", []byte(msg), os.ModePerm)
	}
	if err != nil {
		return errors.Wrap(err, "write go")
	}
	return err
}
